package com.sardine.base.reflection;

import com.sardine.base.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

/**
 * 上海京东到家元信信息技术有限公司<br>
 * Created with IntelliJ IDEA.
 * Date:2018/3/1
 * Time:20:20
 *
 * @author daifeng<br>
 *         daifeng@imdada.cn
 * @since JDK 1.7 <br>
 * Copyright (c) 2018 imdada System Incorporated All Rights Reserved.
 */
public class ReflectionUtils {
    private static final Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);
    public ReflectionUtils() {
    }

    public static Object getFieldValue(Object target, String fieldName) throws Exception {
        if(target != null && fieldName != null && fieldName != "") {
            String[] subFieldNameArray = fieldName.split("\\.");
            Object result = target;
            boolean methodExist = false;
            String[] arr$ = subFieldNameArray;
            int len$ = subFieldNameArray.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                String subFieldName = arr$[i$];
                if(result == null || subFieldName == null || subFieldName == "") {
                    return null;
                }

                if(result instanceof Map) {
                    if(!((Map)result).containsKey(subFieldName)) {
                        return null;
                    }

                    result = ((Map)result).get(subFieldName);
                } else {
                    methodExist = false;
                    Field field = findField(target.getClass(), subFieldName, (Class)null);
                    String methodNamePrefix = field != null && field.getClass().equals(Boolean.TYPE)?"is":"get";
                    String methodName = methodNamePrefix + capitalize(subFieldName);
                    Method method = getDeclaredMethod(target.getClass(), methodName);
                    if(method != null) {
                        result = method.invoke(result, new Object[0]);
                        methodExist = true;
                    } else if(field != null) {
                        field.setAccessible(true);
                        result = field.get(result);
                        methodExist = true;
                    }
                }
            }

            if(!methodExist) {
                return null;
            } else {
                return result;
            }
        } else {
            return null;
        }
    }

    private static String capitalize(String str) {
        return str != null && str.length() != 0?str.substring(0, 1).toUpperCase() + str.substring(1):null;
    }

    public static void setFieldValue(Object target, String fieldName, Object value) throws Exception {
        if(target != null && fieldName != null && fieldName != "") {
            String methodName = "set" + capitalize(fieldName);
            Method invokeMethod = getDeclaredMethod(target.getClass(), methodName);
            if(invokeMethod == null) {
                logger.warn(target.getClass().getName() + "." + methodName + " not exists");
                Field field = findField(target.getClass(), fieldName, (Class)null);
                if(field == null) {
                    logger.warn(target.getClass().getName() + "." + fieldName + " not exists");
                    return;
                }

                field.setAccessible(true);
                field.set(target, value);
            }

            invokeMethod.setAccessible(true);
            invokeMethod.invoke(target, new Object[]{value});
        }
    }

    public static Field findField(Class<?> clazz, String name, Class<?> type) {
        if(clazz == null) {
            throw new IllegalArgumentException("Class must not be null");
        } else if(name == null && type == null) {
            throw new IllegalArgumentException("Either name or type of the field must be specified");
        } else {
            for(Class searchType = clazz; !Object.class.equals(searchType) && searchType != null; searchType = searchType.getSuperclass()) {
                Field[] fields = searchType.getDeclaredFields();
                Field[] arr$ = fields;
                int len$ = fields.length;

                for(int i$ = 0; i$ < len$; ++i$) {
                    Field field = arr$[i$];
                    if((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
                        return field;
                    }
                }
            }

            return null;
        }
    }

    public static Method getDeclaredMethod(Class<?> leafClass, String methodName) throws IllegalArgumentException {
        Method[] allMethods = getAllDeclaredMethods(leafClass);
        Method targetMethod = null;
        Method[] arr$ = allMethods;
        int len$ = allMethods.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Method method = arr$[i$];
            if(method.getName().equals(methodName)) {
                targetMethod = method;
                break;
            }
        }

        return targetMethod;
    }

    public static Method[] getAllDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException {
        final ArrayList methods = new ArrayList(32);
        doWithMethods(leafClass, new ReflectionUtils.MethodCallback() {
            public void doWith(Method method) {
                methods.add(method);
            }
        });
        return (Method[])methods.toArray(new Method[methods.size()]);
    }

    public static void doWithMethods(Class<?> clazz, ReflectionUtils.MethodCallback mc) throws IllegalArgumentException {
        doWithMethods(clazz, mc, (ReflectionUtils.MethodFilter)null);
    }

    public static void doWithMethods(Class<?> clazz, ReflectionUtils.MethodCallback mc, ReflectionUtils.MethodFilter mf) throws IllegalArgumentException {
        Method[] methods = clazz.getDeclaredMethods();
        Method[] arr$ = methods;
        int len$ = methods.length;

        int i$;
        for(i$ = 0; i$ < len$; ++i$) {
            Method superIfc = arr$[i$];
            if(mf == null || mf.matches(superIfc)) {
                try {
                    mc.doWith(superIfc);
                } catch (IllegalAccessException var9) {
                    throw new IllegalStateException("Shouldn\'t be illegal to access method \'" + superIfc.getName() + "\': " + var9);
                }
            }
        }

        if(clazz.getSuperclass() != null) {
            doWithMethods(clazz.getSuperclass(), mc, mf);
        } else if(clazz.isInterface()) {
            Class[] var10 = clazz.getInterfaces();
            len$ = var10.length;

            for(i$ = 0; i$ < len$; ++i$) {
                Class var11 = var10[i$];
                doWithMethods(var11, mc, mf);
            }
        }

    }

    public static Object convertFieldValue(Class<?> fieldClass, Object _value) {
        if(_value == null) {
            return null;
        } else {
            String value = _value.toString();
            Object result = null;
            String fieldType = fieldClass.getName();
            if(_value instanceof Integer || !fieldType.equals("int") && !fieldType.equals("java.lang.Integer")) {
                if(!(_value instanceof Float) && (fieldType.equals("float") || fieldType.equals("java.lang.Float"))) {
                    result = Float.valueOf(value != null && value.trim().length() != 0?(float)Double.parseDouble(value):0.0F);
                } else if(!(_value instanceof Double) && (fieldType.equals("double") || fieldType.equals("java.lang.Double"))) {
                    result = Double.valueOf(value != null && value.trim().length() != 0?Double.parseDouble(value):0.0D);
                } else if(!(_value instanceof Long) && (fieldType.equals("long") || fieldType.equals("java.lang.Long"))) {
                    result = Long.valueOf(value != null && value.trim().length() != 0?(long)Double.parseDouble(value):0L);
                } else if(!(_value instanceof BigDecimal) && fieldType.equals("java.math.BigDecimal")) {
                    result = value != null && value.trim().length() != 0?new BigDecimal(value):null;
                } else if(!(_value instanceof Boolean) && (fieldType.equals("boolean") || fieldType.equals("java.lang.Boolean"))) {
                    result = Boolean.valueOf(value != null && value.trim().length() != 0?Boolean.parseBoolean(value):false);
                } else if(!(_value instanceof Date) && fieldType.equals("java.util.Date") && value != null && value.trim().length() > 0) {
                    try {
                        result = DateUtils.parse(value);
                    } catch (ParseException var6) {
                        throw new IllegalArgumentException(String.format("string conveter to date error, unsupport this format, value is[%s]", new Object[]{value}), var6);
                    }
                } else if(_value instanceof Date && fieldType.equals("java.lang.String") && value != null && value.trim().length() > 0) {
                    result = DateUtils.format((Date)_value, "yyyy-MM-dd HH:mm:ss");
                } else {
                    result = value;
                }
            } else {
                result = Integer.valueOf(value != null && value.trim().length() != 0?(int)Double.parseDouble(value):0);
            }

            return result;
        }
    }

    /**
     * 获取一个对象的所有属性
     * @param clazz
     * @return
     */
    public static List<Field> getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }

    /**
     * 获取一个对象的所有属性
     * @param obj
     * @return
     */
    public static List<Field> getAllFields(Object obj) {
        Class clazz = obj.getClass();
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }
    public interface MethodFilter {
        boolean matches(Method var1);
    }

    public interface MethodCallback {
        void doWith(Method var1) throws IllegalArgumentException, IllegalAccessException;
    }
}
